import { cloneDeep } from 'lodash-es'
import md5 from 'md5'
import { reactive } from 'vue'
import { defineStore } from 'pinia'
import { TYPES, on } from '@/utils/state-broadcast'
import API from '@/http'

export const useFeedStore = defineStore('feed', () => {
  /**
   * {
   *   [id]: {
   *     後端傳來的內容
   *   },
   *   ...
   * }
   */
  const feedsMap = reactive(new Map())

  const inRequesting = {}

  /**
   * 取得多筆帖子緩存
   * @param {Array} ids
   */
  async function getFeedList(ids) {
    if (!Array.isArray(ids)) throw new Error('feed ids only accept array type.')

    const result = []
    const unRequestIds = []

    ids.forEach((id) => {
      if (feedsMap.has(id)) result.push(feedsMap.get(id))
      else unRequestIds.push(id)
    })

    if (unRequestIds.length > 0) {
      const idStr = unRequestIds.join(',')
      const hashKey = md5(idStr)

      if (inRequesting[hashKey]) {
        await inRequesting[hashKey]
        result.push(...unRequestIds.map((id) => feedsMap.get(id)))
        return ids.map((id) => result.find((feed) => feed.id === id))
      }

      inRequesting[hashKey] = API.Media.getPostList({ data: { id: idStr, page: 1, limit: 10 } })
        .then((r) => result.push(...syncFeeds(r)))
        .finally(() => inRequesting[hashKey] && delete inRequesting[hashKey])

      await inRequesting[hashKey]
      return ids.map((id) => result.find((feed) => feed.id === id))
    }
    return result
  }

  async function getFeed(id) {
    if (typeof id !== 'number') throw new Error('feed id only accept number type.')

    if (feedsMap.has(id)) {
      return feedsMap.get(id)
    }

    if (inRequesting[id]) {
      await inRequesting[id]
      return feedsMap.get(id)
    }

    inRequesting[id] = API.Media.getPostList({ data: { id, page: 1, limit: 10 } })
      .then((r) => {
        if (r.length !== 1) throw new Error('id是唯一的，查詢卻少於或多於一筆資料，這是不可能的事情🤬')
        const newFeed = r[0]
        feedsMap.set(id, newFeed)
      })
      .finally(() => {
        if (inRequesting[id]) delete inRequesting[id]
      })

    await inRequesting[id]
    return feedsMap.get(id)
  }

  function syncFeeds(feedList) {
    return feedList.map((feed) => {
      const id = feed.id
      if (!feedsMap.has(id)) {
        feedsMap.set(id, {})
      }

      const feedInMap = feedsMap.get(id)
      Object.assign(feedInMap, cloneDeep(feed))

      return feedInMap
    })
  }

  function clear() {
    feedsMap.clear()
  }

  on(TYPES.LIKE, (id, isLike) => {
    if (!feedsMap.has(id)) {
      return
    }

    const feed = feedsMap.get(id)
    feed.liked = isLike
    feed.like += isLike ? 1 : -1
  })

  on(TYPES.FOLLOW, (aff, isFollow) => {
    feedsMap.forEach((data) => {
      if (data.aff === aff) {
        if (data.author) {
          data.author.is_follow = isFollow
        } else {
          data.is_follow = isFollow
        }
      }
    })
  })

  on(TYPES.SUB, (aff, isSubscribe) => {
    feedsMap.forEach((data) => {
      if (data.aff === aff) {
        data.need_subscribe = !isSubscribe
        data.can_play = isSubscribe
      }
    })
  })

  on(TYPES.BUY, (id, isBuy) => {
    if (!feedsMap.has(id)) {
      return
    }

    const feed = feedsMap.get(id)
    API.Home.getVideoPath({ data: { id } }).then(({ video_path, img_path }) => {
      feed.need_buy = !isBuy
      feed.can_play = isBuy
      feed.video_path = video_path
      feed.img_path = img_path
    })
  })

  on(TYPES.BLOCK, (aff, isBlock) => {
    feedsMap.forEach((data) => {
      if (data.aff === aff) {
        if (data.author) {
          data.author.is_block = isBlock
        } else {
          data.is_block = isBlock
        }
      }
    })
  })

  return {
    getFeed,
    getFeedList,
    syncFeeds,
    clear,
  }
})
